// buffer.h

#ifndef BUFFER_H
#define BUFFER_H

#include <stdint.h>
#include <assert.h>
#include <visibility.h>

class __EXPORT Buffer {
public:
	enum {bigEndian = false, littleEndian = true};

	Buffer(uint8_t *baseAddr, int size, bool endian = littleEndian)
		: baseAddr_(baseAddr)
		, size_(size)
		, indexGet_(0)
		, indexPut_(0)
		, endian_(endian)
	{}

	Buffer(const uint8_t *baseAddr, int size, bool endian = littleEndian)
		: baseAddr_(const_cast<uint8_t *>(baseAddr))
		, size_(size)
		, indexGet_(0)
		, indexPut_(0)
		, endian_(endian)
	{}
	
	void setIndex(int value) {indexPut_ = indexGet_ = value;}
	void setIndexForGet(int value) {indexGet_ = value;}
	void setIndexForPut(int value) {indexPut_ = value;}
	int getIndexForPut() const {return indexPut_;}
	

// get methods ////////////////////////////////////////////////////
	
	uint8_t getUint8() {
		return (indexGet_ < size_) ? baseAddr_[indexGet_++] : 0;
	}
	
	uint16_t getUint16() {
		uint16_t res = (endian_ == littleEndian ? 1 : 256) * getUint8();
		res += (endian_ == littleEndian ? 256 : 1) * getUint8();
		return res;
	}
	
	uint32_t getUint32() {
		uint32_t res = (endian_ == littleEndian ? 1 : 65536UL) * getUint16();
		res += (endian_ == littleEndian ? 65536UL : 1) * getUint16();
		return res;
	}
	
	float getFloat32() {
		float f;
		*reinterpret_cast<uint32_t *>(&f) = getUint32();
		return f;
	}
		
	double getFloat64() {
		double df;
		*(reinterpret_cast<uint32_t *>(&df) + (endian_ == littleEndian ? 0 : 1)) = getUint32();
		*(reinterpret_cast<uint32_t *>(&df) + (endian_ == littleEndian ? 1 : 0)) = getUint32();
		return df;
	}

	uint8_t getUint8(int index) {
		indexGet_ = index;
		return getUint8();
	}
	
	uint16_t getUint16(int index) {
		indexGet_ = index;
		return getUint16();
	}
	
	uint32_t getUint32(int index) {
		indexGet_ = index;
		return getUint32();
	}
	
	float getFloat32(int index) {
		indexGet_ = index;
		return getFloat32();
	}
		
	double getFloat64(int index) {
		indexGet_ = index;
		return getFloat64();
	}

	
// put methods ////////////////////////////////////////////////////
	
	void putUint8(uint8_t val) {
		if (indexPut_ < size_)
			baseAddr_[indexPut_++] = val;
	}
	
	void putUint16(uint16_t val) {
		if (endian_ == littleEndian) {
			putUint8(val & 0xFF);
			putUint8(val >> 8);
		} else {
			putUint8(val >> 8);
			putUint8(val & 0xFF);
		}
	}
	
	void putUint32(uint32_t val) {
		if (endian_ == littleEndian) {
			putUint16(val & 0xFFFF);
			putUint16(val >> 16);
		} else {
			putUint16(val >> 16);
			putUint16(val & 0xFFFF);
		}
	}
	
	void putFloat32(float val) {
		putUint32(*reinterpret_cast<uint32_t *>(&val));
	}
		
	void putFloat64(double val) {
		putUint32(*(reinterpret_cast<uint32_t *>(&val) + (endian_ == littleEndian ? 0 : 1)));
		putUint32(*(reinterpret_cast<uint32_t *>(&val) + (endian_ == littleEndian ? 1 : 0)));
	}
	
	void putUint8(int index, uint8_t val) {
		indexPut_ = index;
		putUint8(val);
	}
	
	void putUint16(int index, uint16_t val) {
		indexPut_ = index;
		putUint16(val);
	}
	
	void putUint32(int index, uint32_t val) {
		indexPut_ = index;
		putUint32(val);
	}
	
	void putFloat32(int index, float val) {
		indexPut_ = index;
		putFloat32(val);
	}
		
	void putFloat64(int index, double val) {
		indexPut_ = index;
		putFloat64(val);
	}
	

private:
	Buffer();
	uint8_t *baseAddr_;
	const int size_;
	int indexGet_;
	int indexPut_;
	const bool endian_;
};

#endif
